package com.bfly.core.config;

import com.alibaba.fastjson.JSONArray;
import com.bfly.common.json.JsonUtil;
import com.bfly.core.cache.AdminMenuCacheUtil;
import com.bfly.core.cache.CityCacheUtil;
import com.bfly.core.cache.MenuCacheUtil;
import com.bfly.core.cache.SysConfigCacheUtil;
import com.bfly.core.context.CacheableThreadLocal;
import com.bfly.core.context.event.*;
import com.bfly.mall.admin.dto.MenuListDTO;
import com.bfly.mall.admin.entity.Admin;
import com.bfly.mall.admin.entity.Role;
import com.bfly.mall.admin.service.IAdminService;
import com.bfly.mall.admin.service.IMenuService;
import com.bfly.mall.system.dto.*;
import com.bfly.mall.system.entity.City;
import com.bfly.mall.system.enums.SysConfigType;
import com.bfly.mall.system.service.ICityService;
import com.bfly.mall.system.service.ISysConfigService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.core.task.TaskExecutor;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 应用程序事件监听器
 *
 * @author 长沙商脉科技有限公司
 * @date 2020/10/16 13:28
 */
@Configuration
public class ApplicationEventListener {

    private Logger logger = LoggerFactory.getLogger(ApplicationEventListener.class);

    /**
     * 应用程序启动事件
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/8/18 15:41
     */
    @EventListener
    public void applicationStarted(ApplicationReadyEvent event) {
        ApplicationContext ctx = event.getApplicationContext();

        TaskExecutor taskExecutor = ctx.getBean("taskExecutor", TaskExecutor.class);
        taskExecutor.execute(() -> {

            // 程序启动触发加载系统配置到缓存
            ctx.publishEvent(new SysConfigReloadEvent(ctx));

            // 程序启动触发加载系统任务数据到数据库
            ctx.publishEvent(new SysTaskReloadEvent(ctx));

            // 程序启动触发加载菜单事件
            ctx.publishEvent(new MenuReloadEvent(ctx));

            // 程序启动触发加载省份城市数据到缓存
            ctx.publishEvent(new CityReloadEvent(ctx));
        });
    }

    /**
     * 系统配置参数重新加载事件
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/16 13:29
     */
    @EventListener
    public void sysConfigReload(SysConfigReloadEvent event) {
        ApplicationContext ctx = (ApplicationContext) event.getSource();
        ISysConfigService sysConfigService = ctx.getBean(ISysConfigService.class);

        SystemConfigDTO systemConfig = sysConfigService.getSystemConfig();
        OrderConfigDTO orderConfig = sysConfigService.getOrderConfig();
        WaterMarkDTO waterMarkConfig = sysConfigService.getWaterMarkConfig();
        WXMiniProConfigDTO wxMiniProConfig = sysConfigService.getWxMiniProConfig();
        WXMPConfigDTO wxmpConfig = sysConfigService.getWxMpConfig();
        WXPayConfigDTO wxPayConfig = sysConfigService.getWxPayConfig();
        if (systemConfig != null) {
            SysConfigCacheUtil.setData(SysConfigType.SYSTEM, systemConfig);
            logSystemConfig(systemConfig);
        }
        if (orderConfig != null) {
            SysConfigCacheUtil.setData(SysConfigType.ORDER, orderConfig);
        }
        if (waterMarkConfig != null) {
            SysConfigCacheUtil.setData(SysConfigType.WATER_MARK, waterMarkConfig);
        }
        if (wxMiniProConfig != null) {
            SysConfigCacheUtil.setData(SysConfigType.WX_MINIPRO, wxMiniProConfig);
            logWxMiniProConfig(wxMiniProConfig);
        }
        if (wxmpConfig != null) {
            SysConfigCacheUtil.setData(SysConfigType.WX_MP, wxmpConfig);
            logWxMpConfig(wxmpConfig);
        }
        if (wxPayConfig != null) {
            SysConfigCacheUtil.setData(SysConfigType.WX_PAY, wxPayConfig);
            logWxPayConfig(wxPayConfig);
        }
        logger.info("加载系统配置参数到缓存成功!");
    }

    /**
     * 系统参数配置检查
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/10 9:19
     */
    private void logSystemConfig(SystemConfigDTO configDTO) {
        if (StringUtils.isEmpty(configDTO.getPicServer())) {
            logger.warn("图片服务器地址尚未配置!");
        }
        if (StringUtils.isEmpty(configDTO.getTenCentMapKey())) {
            logger.warn("腾讯地图API KEY尚未配置!");
        }
    }

    /**
     * 微信小程序参数配置检查
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/10 9:18
     */
    private void logWxMiniProConfig(WXMiniProConfigDTO wxMiniProConfigDTO) {
        if (StringUtils.isEmpty(wxMiniProConfigDTO.getAppId())) {
            logger.warn("小程序APP ID尚未配置!");
        }
        if (StringUtils.isEmpty(wxMiniProConfigDTO.getAppSecret())) {
            logger.warn("小程序APP Secret尚未配置!");
        }
    }

    /**
     * 微信公众号参数配置检查
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/17 22:33
     */
    private void logWxMpConfig(WXMPConfigDTO wxmpConfigDTO) {
        if (StringUtils.isEmpty(wxmpConfigDTO.getAppId())) {
            logger.warn("公众号APP ID尚未配置!");
        }
        if (StringUtils.isEmpty(wxmpConfigDTO.getAppSecret())) {
            logger.warn("公众号APP Secret尚未配置!");
        }
    }

    /**
     * 微信支付参数检查
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/17 22:33
     */
    private void logWxPayConfig(WXPayConfigDTO wxPayConfigDTO) {
        if (StringUtils.isEmpty(wxPayConfigDTO.getMchId())) {
            logger.warn("微信支付 商户号Mch ID尚未配置!");
        }
        if (StringUtils.isEmpty(wxPayConfigDTO.getApiKeyV3())) {
            logger.warn("微信支付 Api Key尚未配置!");
        }
        if (StringUtils.isEmpty(wxPayConfigDTO.getCertP12()) || StringUtils.isEmpty(wxPayConfigDTO.getCertPem()) || StringUtils.isEmpty(wxPayConfigDTO.getKeyPem())) {
            logger.warn("微信支付 API证书尚未配置!");
        }
        if (StringUtils.isEmpty(wxPayConfigDTO.getSerialNo())) {
            logger.warn("微信支付 证书序列号尚未配置!");
        }
        if (StringUtils.isEmpty(wxPayConfigDTO.getNotifyUrl())) {
            logger.warn("微信支付 支付回调URL尚未配置!");
        }
    }

    /**
     * 系统菜单重新加载事件
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/16 13:29
     */
    @EventListener
    public void menuReload(MenuReloadEvent event) {
        ApplicationContext ctx = (ApplicationContext) event.getSource();
        IMenuService menuService = ctx.getBean(IMenuService.class);
        List<MenuListDTO> list = menuService.getFunctionMenu();
        MenuCacheUtil.setData(list);
        logger.info("加载系统菜单到缓存成功!");
    }

    /**
     * 省份城市数据加载事件
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/24 12:09
     */
    @EventListener
    public void cityReload(CityReloadEvent event) {
        ApplicationContext ctx = (ApplicationContext) event.getSource();
        ICityService cityService = ctx.getBean(ICityService.class);

        Map<String, Object> param = new HashMap<>(1);
        param.put("level", City.PROVINCE);  // 全国省份

        Map<String, Boolean> sortMap = new HashMap<>(1);
        sortMap.put("id", true);
        CacheableThreadLocal.set(false);

        List<City> districts = cityService.getList(param, null, sortMap);
        CityCacheUtil.setData(districts, City.DISTRICT);

        List<City> cities = deepCopy(districts);
        cities.forEach(city -> filterLevelChildren(city, City.CITY));
        CityCacheUtil.setData(cities, City.CITY);

        List<City> provinces = deepCopy(districts);
        provinces.forEach(city -> filterLevelChildren(city, City.PROVINCE));
        CityCacheUtil.setData(provinces, City.PROVINCE);

        CacheableThreadLocal.clear();
        logger.info("加载省份城市数据到缓存成功!");
    }

    /**
     * 过滤掉指定级别的子项
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/24 16:04
     */
    private City filterLevelChildren(City city, int level) {
        if (city.getLevel() == level) {
            city.setChildren(null);
        }
        List<City> list = city.getChildren();
        if (list != null) {
            for (City c : list) {
                filterLevelChildren(c, level);
            }
        }
        return city;
    }

    /**
     * list深拷贝
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/24 15:46
     */
    public List<City> deepCopy(List<City> src) {
        try {
            JSONArray array = JsonUtil.toJsonFilterForArray(src);
            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(byteOut);
            out.writeObject(array);

            ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
            ObjectInputStream in = new ObjectInputStream(byteIn);
            array = (JSONArray) in.readObject();
            List<City> list = JsonUtil.parseStrToList(array.toJSONString(), City.class);
            return list;
        } catch (Exception e) {
            new RuntimeException(e);
        }
        return null;
    }

    /**
     * 加载登录管理员菜单到缓存
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/28 21:59
     */
    @EventListener
    public void adminMenuReload(AdminMenuReloadEvent event) {
        String userName = event.getUserName();
        ApplicationContext ctx = (ApplicationContext) event.getSource();
        IAdminService adminService = ctx.getBean(IAdminService.class);
        Admin admin = adminService.getAdmin(userName);
        if (admin == null) {
            logger.warn(String.format("%s 管理员信息不存在,加载菜单失败!"));
            return;
        }
        IMenuService menuService = ctx.getBean(IMenuService.class);
        boolean isSupper = false;
        for (Role role : admin.getRoles()) {
            if (role.isSys()) {
                isSupper = true;
                break;
            }
        }
        List<MenuListDTO> list;
        // 如果是超级管理员则加载全部菜单到缓存
        if (isSupper) {
            list = menuService.getFunctionMenu();
        } else {
            list = menuService.getFunctionMenuByAdmin(admin.getId());
        }
        AdminMenuCacheUtil.setData(admin.getUserName(), list);
        logger.info(String.format("%s 权限加载成功!", admin.getUserName()));
    }
}
